﻿#region Copyright © 2011 Prageeth Silva <prageeth@thenewcoders.com>
/*
 * This software is provided 'as-is', without any express or implied warranty.
 * In no event will the author(s) be held liable for any damages arising from
 * the use of this software.
 * 
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 * 
 *   1. The origin of this software must not be misrepresented; you must not
 *      claim that you wrote the original software. If you use this software
 *      in a product, an acknowledgment in the product documentation would be
 *      appreciated but is not required.
 * 
 *   2. Altered source versions must be plainly marked as such, and must not
 *      be misrepresented as being the original software.
 * 
 *   3. This notice may not be removed or altered from any source distribution.
 */
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
using SourcePlus.Themes;
using SourcePlus.Core;

namespace SourcePlus.RtfPdf
{

    public class RtfToPdf : IPdfPageEvent
    {
        
        public int Failures { get; private set; }

        public int Successes { get; private set; }

        public string TrimDirectory { get; set; }

        public string AddDirectory { get; set; }

        public Rectangle PageSize { get; set; }

        public Rectangle Margins { get; set; }


        private int CurrentPage { get; set; }

        private Font FontHeaderFooter { get; set; }

        private BaseColor ColorHeaderFooter { get; set; }

        private int TrimDirectoryLength { get; set; }
        
        private SourceFile CurrentSourceFile = null;


        public static int TotoalRegisteredFonts { get; private set; }
        

        static RtfToPdf()
        {

            LoadSystemFonts();
            
        }

        public static void LoadSystemFonts()
        {

            string fontPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Windows), "Fonts");
            TotoalRegisteredFonts = FontFactory.RegisterDirectory(fontPath, false);

        }

        public RtfToPdf()
        {

            this.Failures = 0;
            this.Successes = 0;
            this.CurrentPage = 1;
            this.FontHeaderFooter = null;

            this.TrimDirectory = null;
            this.AddDirectory = null;
            
            this.PageSize = iTextSharp.text.PageSize.A4;
            // (left, bottom, right, top)
            this.Margins = new Rectangle(60.0f, 55.0f, 60.0f, 60.0f);

        }
        
        private void Initialise()
        {

            this.ColorHeaderFooter = new BaseColor(System.Drawing.Color.Gray);
            this.FontHeaderFooter = FontFactory.GetFont("Consolas", 9, (int)Font.NORMAL, this.ColorHeaderFooter);
            this.TrimDirectoryLength = (this.TrimDirectory != null) ? this.TrimDirectory.Length : 0;
            if (this.AddDirectory == null) { this.AddDirectory = string.Empty; }
            this.CurrentPage = 1;

        }

        public bool Convert(string destination, SourceFile[] files, bool skipOnError)
        {

            if (destination == null || files == null) { return false; }

            FileStream dest = null;
            dest = new FileStream(destination, FileMode.Create);                
            
            return this.Convert(dest, files, skipOnError);

        }

        public bool Convert(Stream destination, SourceFile[] files, bool skipOnError)
        {

            // check validity
            if (destination == null || files == null) { return false; }

            // initialise internal values
            this.Initialise();

            // create a new document  
            Document doc = new Document(this.PageSize,
                                            this.Margins.Left, this.Margins.Right,
                                            this.Margins.Top, this.Margins.Bottom);

            // try to open the document
            PdfWriter writer = null;
            try
            {
                writer = PdfWriter.GetInstance(doc, destination);
                writer.PageEvent = this;
                doc.Open();
            }
            catch
            {
                return false;
            }
            
            // reset counters
            this.Failures = 0;
            this.Successes = 0;
            
            // add the files in order
            foreach (SourceFile source in files)
            {

                // reset file specific counter
                // new page increments the value
                this.CurrentPage = 1;
                                
                // convert and add the try to the doc
                // check if it fails and needs to be skipped?
                if (!this.ConvertRtfDoc(writer, doc, source) && !skipOnError)
                {
                    this.Failures++;
                    return false;
                }
                else
                {
                    this.Successes++;
                }

            }

            // try to close the document
            try
            {
                doc.Close();
            }
            catch
            {
                return false;
            }
            
            // successful
            return true;

        }
        
        private bool ConvertRtfDoc(PdfWriter writer, Document doc, SourceFile input)
        {

            if (input == null || !input.IsValid) { return false; }

            try
            {

                // parse the rtf
                RtfParser parser = new RtfParser();
                RtfObject rtf = parser.Parse(input.RtfContent);
                if (rtf == null) { return false; }
                
                this.CurrentSourceFile = input;

                // add the content (can be mulptiple pages)
                if (!this.CreateContent(writer, doc, input, rtf))
                {
                    return false;
                }
                                                
            }
            catch
            {
                return false;
            }

            return true;
            
        }

        private bool CreateContent(PdfWriter writer, Document doc, SourceFile input, RtfObject rtf)
        {

            try
            {

                Paragraph p = new Paragraph();
                p.MultipliedLeading = 0.8f;

                bool firstline = true;
                foreach (RtfLine line in rtf.Lines)
                {

                    if (!firstline)
                    {
                        p.Add(Environment.NewLine);
                    }
                    else
                    {
                        firstline = false;
                    }

                    foreach (RtfToken token in line.Tokens)
                    {

                        BaseColor color = new BaseColor(token.Color);
                        Font font = FontFactory.GetFont(token.Font.Name, token.Font.Size,
                                                                (int)token.Font.Style, color);
                        p.Add(new Phrase(token.Value, font));

                    }

                }

                doc.Add(p);
                
            }
            catch
            {
                return false;
            }

            return true;
            
        }

        private bool CreateHeader(PdfWriter writer, Document doc, SourceFile input)
        {

            try
            {

                PdfContentByte cb = writer.DirectContent;
                Rectangle page = doc.PageSize;                

                // draw line below header
                float x, y;
                x = page.Left;
                y = page.Height - doc.TopMargin + 10.0f;
                cb.MoveTo(x, y);
                x = page.Right;
                cb.LineTo(x, y);
                cb.SetColorStroke(this.ColorHeaderFooter);
                cb.Stroke();

                // add text in a table
                PdfPTable table = new PdfPTable(2);
                float[] widths = new float[] { 1.8f, 0.2f };
                table.TotalWidth = page.Width - doc.LeftMargin - doc.RightMargin;
                table.LockedWidth = true;
                table.SetWidths(widths);

                // add directory
                string dir = this.AddDirectory + input.Directory.Substring(this.TrimDirectoryLength);
                PdfPCell cellDir = new PdfPCell(new Phrase(dir, this.FontHeaderFooter));
                cellDir.Colspan = 2;
                cellDir.Border = Rectangle.NO_BORDER;
                cellDir.HorizontalAlignment = Element.ALIGN_LEFT;
                table.AddCell(cellDir);

                // add file name
                PdfPCell cellFile = new PdfPCell(new Phrase(input.FileName, this.FontHeaderFooter));
                cellFile.HorizontalAlignment = Element.ALIGN_LEFT;
                cellFile.Border = Rectangle.NO_BORDER;
                table.AddCell(cellFile);

                // add current page number
                PdfPCell cellPageNum = new PdfPCell(new Phrase(this.CurrentPage.ToString(), this.FontHeaderFooter));
                cellPageNum.HorizontalAlignment = Element.ALIGN_RIGHT;
                cellPageNum.Border = Rectangle.NO_BORDER;
                table.AddCell(cellPageNum);

                // write the table in a column
                table.WriteSelectedRows(0, 2, doc.LeftMargin, page.Height - (this.FontHeaderFooter.Size * 1.5f), cb);
                
            }
            catch
            {
                return false;
            }

            return true;

        }
        
        private bool CreateFooter(PdfWriter writer, Document doc, SourceFile input)
        {

            try
            {

                PdfContentByte cb = writer.DirectContent;

                Rectangle page = doc.PageSize;
                
                // draw line below header
                float x, y;
                x = page.Right;
                y = doc.BottomMargin - 10.0f;
                cb.MoveTo(x, y);
                x = page.Left;
                cb.LineTo(x, y);
                cb.SetColorStroke(this.ColorHeaderFooter);
                cb.Stroke();

                // draw page number
                string text = "[ " + writer.CurrentPageNumber + " ]";
                Chunk pageNum = new Chunk(text, this.FontHeaderFooter);
                ColumnText col = new ColumnText(cb);
                col.SetSimpleColumn(page.Left, page.Bottom, page.Width, doc.BottomMargin / 2.0f + this.FontHeaderFooter.Size);
                col.AddText(pageNum);
                col.Alignment = Element.ALIGN_CENTER;                
                col.Go();
               
            }
            catch
            {
                return false;
            }

            return true;

        }
                
        public void OnEndPage(PdfWriter writer, Document document)
        {
            
            // write header and footer
            this.CreateHeader(writer, document, this.CurrentSourceFile);
            this.CreateFooter(writer, document, this.CurrentSourceFile);

        }

        public void OnStartPage(PdfWriter writer, Document document)
        {
            // increment the pages
            this.CurrentPage++;
        }

        public void OnChapter(PdfWriter writer, Document document, float paragraphPosition, Paragraph title)
        {
            // do nothing
        }

        public void OnChapterEnd(PdfWriter writer, Document document, float paragraphPosition)
        {
            // do nothing
        }

        public void OnCloseDocument(PdfWriter writer, Document document)
        {
            // do nothing
        }

        public void OnGenericTag(PdfWriter writer, Document document, Rectangle rect, string text)
        {
            // do nothing
        }

        public void OnOpenDocument(PdfWriter writer, Document document)
        {
            // do nothing
        }

        public void OnParagraph(PdfWriter writer, Document document, float paragraphPosition)
        {
            // do nothing
        }
        
        public void OnParagraphEnd(PdfWriter writer, Document document, float paragraphPosition)
        {
            document.NewPage();
        }

        public void OnSection(PdfWriter writer, Document document, float paragraphPosition, int depth, Paragraph title)
        {
            // do nothing
        }

        public void OnSectionEnd(PdfWriter writer, Document document, float paragraphPosition)
        {
            // do nothing
        }

    }

}

